/*
 * main.c
 * 
 * Copyright 2012 Kamil Cukrowski <kamil@dyzio.pl>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * based on Music On Console
 * Thank you Damian Pietras <daper@daper.net>
 */

#define MSGMOCSTAT_VERSION "1.3.7"

#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <arpa/inet.h>
#include <msgserv.h>
#include "mocp-lib.h"
#include <time.h>


#define ON_SCREEN 0 /* on which scren we are desplyed, needed for cgram flushing */
#define MSGLCD_PRIORITY MSG_NOTICE+1 /* display priority to msgserver */
#define TIMER_TV_SEC 0 /* timer seconds */
#define TIMER_TV_USEC 250000000 /* time useconds */

#define MY_NAME "msgmocstat"
#define PINFO(...) printf( __VA_ARGS__ );
#define PDEBUG(str, ...) PDEBUGL(1, str, ##__VA_ARGS__);
#define PDEBUGL(x, str, ...) do{ if ( debug >= x ) _INTERNAL_LOGIT(str, ##__VA_ARGS__); }while(0)
#define PERROR(str, ...) do { printf(MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("error %d:%s \n", errno, strerror(errno)); }while(0)
#define _INTERNAL_LOGIT(str, ...) do { printf( MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("\n"); }while(0)
#define PERR PERROR

/* sockets for moc and lcd servers */
static int moc_sock = -1;
/* moc_sock = -1 -> moc server is down */
/* moc_sock = -2 -> moc server is busy */
/* moc_sock = -3 -> moc_server sended EV_SRV_ERROR */
/* moc_sock = -4 -> unspecifed error occured on moc server */
static int lcd_sock = -1;
static in_addr_t server_ip;
int debug = 0;

/* something changed in the view ? */
static int view_in_this_loop = 1;

/* position of the string rewinded on lcds.
 * gets incremented by signal clock */
static int position = 0;

static int clock_signal = 0;
static timer_t timer_id = 0;

/* info about current played (paused or stoped) file in moc */
static struct file_info curr_file;
static struct event_queue events;
static struct server_options_type srv_options;

/***** signal handling funcitons and exit functions *****/

#define logit PINFO

static void safe_exit()
{
	if ( moc_sock > 0 ) {
		send (moc_sock, (void *)CMD_DISCONNECT, sizeof(int), MSG_NOSIGNAL);
		close(moc_sock);
	}
	if ( lcd_sock > 0 )
		msglcd_disconnect(lcd_sock);
	
	event_queue_free(&events);
	
	exit(0);
}

#define fatal(format, ...)\
do {\
	PERROR(format, ##__VA_ARGS__);\
	safe_exit();\
} while(0)


static void clock_handler(int signo) 
{
   clock_signal=1;
}

static void sig_hup (int param) 
{
	PINFO("Got signal %d. Going down", param);
	safe_exit();
}

/***** end of signal handling functions *****/

/******* timer, clock events, working on signal SIGUSR1 *******/

static void clock_set_signal() 
{  /* Establish handler for timer signal */
  struct sigaction sa;
  sa.sa_flags = SA_SIGINFO;
  sa.sa_handler = clock_handler;
  sigemptyset(&sa.sa_mask);
  if (sigaction(SIGUSR1, &sa, NULL) == -1)
    fatal("sigaction");
}

static void clock_unset_signal() 
{
  struct sigaction sa = {.sa_handler = SIG_IGN};
  sigaction(SIGUSR1, &sa, NULL);
}

static void clock_block_signal()
{
   /* Block timer signal temporarily */
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGUSR1);
  if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
    fatal("sigprocmask");

}

static void clock_unblock_signal()
{
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGUSR1);
  if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
    fatal("sigprocmask");

}

static timer_t clock_start() 
{
  /* Make sure there arens running two clock on the same var */
  timer_t timerid;
  struct sigevent sev;
  struct itimerspec its;

  clock_set_signal();
  clock_block_signal();

  /* Create the timer */
  sev.sigev_notify = SIGEV_SIGNAL;
  sev.sigev_signo = SIGUSR1;
  sev.sigev_value.sival_ptr = &timerid;
  if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1)
    fatal ("timer_create");

   /* Start the timer */
   its.it_value.tv_sec = TIMER_TV_SEC;
   its.it_value.tv_nsec = TIMER_TV_USEC;
   its.it_interval.tv_sec = its.it_value.tv_sec;
   its.it_interval.tv_nsec = its.it_value.tv_nsec;
   if (timer_settime(timerid, 0, &its, NULL) == -1)
     fatal ("timer_settime");


  return timerid;
}

static void clock_stop(timer_t* timerid) 
{
	/* null the variable */
	if(*timerid == 0) return;
	clock_unset_signal();
	clock_unblock_signal();
	if ( timer_delete(*timerid) )
		fatal(" deleting timer ");
	*timerid = 0;
}

/********* end of timer clock events ********/

/********* moc server functions *********/
static void send_int_to_srv (const int num) 
{
	if (moc_sock < 0) return;
	if (!send_int(moc_sock, num)) {
		PERR ("Can't send() int to the moc server %s \n", "a"); //strerror(errno));
		moc_sock = -5;
	}
}

static void send_str_to_srv (const char *str) 
{
	if (moc_sock < 0) return;
	if (!send_str(moc_sock, str)) {
		PERR ("Can't send() string to the server: %s.", strerror(errno));
		moc_sock = -5;
	}
}

static int get_int_from_srv () 
{
	int num = 0;
	if (moc_sock < 0) return(0);
	if (!get_int(moc_sock, &num)) {
		PERR ("Can't receive value from the server: %s \n", strerror(errno));
		moc_sock=-5;
	}
	return num;
}

static char *get_str_from_srv () {
	char *str;
	if (moc_sock < 0) return(0);
	str = get_str (moc_sock);
	if (!str) {
		PERR ("Can't receive string from the moc server: %s \n", strerror(errno));
		moc_sock=-5;
	}
	return str;
}

static struct tag_ev_response *recv_tags_data_from_srv () {
	struct tag_ev_response *r;
	if (moc_sock < 0) return(0);
	r = (struct tag_ev_response *)xmalloc (sizeof(struct tag_ev_response));
	r->file = get_str_from_srv ();
	if (!(r->tags = recv_tags(moc_sock))) {
		PERR ("Can't receive tags event's data from the server: %s", strerror(errno));
		moc_sock=-5;
	}
	return r;
}
static void *handle_event_data (const int type) {
	switch (type) {
	case EV_PLIST_ADD:
		clear_item_from_srv (moc_sock);
		break;
	case EV_PLIST_DEL:
		if (!(get_str(moc_sock))) {
			logit ("Error while receiving 'filename' data from the moc server.");
		}
	case EV_STATUS_MSG:
		return get_str_from_srv ();
	case EV_FILE_TAGS:
		return recv_tags_data_from_srv ();
	case EV_PLIST_MOVE:
		break;
	}
	
	return NULL;
}

static void wait_for_data () {/* Wait for EV_DATA handling other events. */
	int event;
	do {
		event = get_int_from_srv ();
		if (!event) 
			return;
		if (event != EV_DATA)
			event_push (&events, event, handle_event_data(event));
	} while (event != EV_DATA);
}
/* Get an integer value from the server that will arrive after EV_DATA. */
static int get_data_int () {
	wait_for_data ();
	return get_int_from_srv ();
}
/* Get a string value from the server that will arrive after EV_DATA. */
static char *get_data_str () {
	wait_for_data ();
	return get_str_from_srv ();
}

static char *get_err_mesg() 
{
	send_int_to_srv (CMD_GET_ERROR);
	return get_data_str (moc_sock);
}

static int get_state () 
{
	send_int_to_srv (CMD_GET_STATE);
	return get_data_int ();
}
static int get_channels () 
{
	send_int_to_srv (CMD_GET_CHANNELS);
	return get_data_int ();
}
static int get_rate () 
{
	send_int_to_srv (CMD_GET_RATE);
	return get_data_int ();
}
static int get_bitrate () 
{
	send_int_to_srv (CMD_GET_BITRATE);
	return get_data_int ();
}
static int get_curr_time () 
{
	send_int_to_srv (CMD_GET_CTIME);
	return get_data_int ();
}
static char *get_curr_file () 
{
	send_int_to_srv (CMD_GET_SNAME);
	return get_data_str ();
}
static struct file_tags *get_tags (const char *file) 
{
	struct file_tags *tags = NULL;
		
	/* asking moc server for tags */
	assert (file != NULL);
	send_int_to_srv (CMD_GET_FILE_TAGS);
	send_str_to_srv (file);
	send_int_to_srv (TAGS_COMMENTS | TAGS_TIME);
	if(debug) 
		logit ("Asking for tags for %s.", file);
	
	while (!tags) {
		int type = get_int_from_srv ();
		void *data = handle_event_data (type);
		
		if (type == EV_FILE_TAGS) {
			struct tag_ev_response *ev = (struct tag_ev_response *)data;
			if (!strcmp(ev->file, file)) {
				tags = tags_dup (ev->tags);
			}
			free_tag_ev_data (ev);
		}
	}
	return tags;
}
static struct file_tags *recv_tags_from_srv ()
{
	struct file_tags *tags = recv_tags (moc_sock);

	if (!tags)
		PDEBUG("Can't receive tags from the server!");

	return tags;
}
static struct file_tags *get_data_tags ()
{
	wait_for_data ();
	return recv_tags_from_srv ();
}

static struct server_options_type get_server_options () 
{
	struct server_options_type options;
	int value=0;
	send_int_to_srv (CMD_GET_OPTION);
	send_str_to_srv ("Shuffle");
	value = get_data_int ();
	options.shuffle=value;
	send_int_to_srv (CMD_GET_OPTION);
	send_str_to_srv ("Repeat");
	value = get_data_int ();
	options.repeat=value;
	send_int_to_srv (CMD_GET_OPTION);
	send_str_to_srv ("AutoNext");
	value = get_data_int ();
	options.autonext=value;
	return options;
}
static char *get_filename(char *path) 
{
		const char *ext, *name;
		char buffer[256];
		if(!path) 
			return (NULL);
		ext = strrchr(path, '.');
		name = strrchr(path, '/') + 1;
		if ( ext < name ) 
			return xstrdup(path);
		strncpy(buffer, name, ext-name);
		memset(buffer+(ext-name), 0, strlen(ext)*sizeof(char));
		return xstrdup(buffer);
}

/* Is the string a URL? */
inline int is_url (const char *str)
{
        return !strncasecmp (str, "http://", sizeof ("http://") - 1)
                || !strncasecmp (str, "ftp://", sizeof ("ftp://") - 1);
}

/* Use new tags for current file title (for Internet streams). */
static void update_curr_tags ()
{
	if (curr_file.file && is_url(curr_file.file)) {
		struct file_tags *tags = NULL;
		send_int_to_srv(CMD_GET_TAGS);
		tags = get_data_tags ();
		
		if ( tags && tags->title) {
			char *ptr = moc_build_title(tags);
			if ( strcmp(curr_file.title, ptr) ) {
				/* string changed */
				free(curr_file.title); /* rememeber to.. */
				curr_file.title = ptr;
				view_in_this_loop = 1; /* puffff, here it is! */
			} else {
				/* string do not change */
				free(ptr);
			}
		}
		free(tags); /* this too */
	}
}


static void handle_place(char *place, char point)
{
        int i;
        (*place) = point;
        for (i=1; place[i] != '\0'; ++i)
                place[i] = place[i+1];
}

static void parse_unreadable_characters(char *buff)
{
        /*to do: cgram! */
        char *place;
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'a');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'c');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'e');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'l');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'n');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'o');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 's');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'z');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'z');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'A');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'C');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'E');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'L');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'N');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'O');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'S');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'Z');
        while( (place = strstr(buff, "??")) )
                handle_place(place, 'Z');

}

static void build_song_info()
{
	/* curr file is a global variable */
	struct file_tags *tags;
	
	if (!curr_file.file) {
		curr_file.total_time = -1;
		free(curr_file.title);
		return;
	}
    
	tags = get_tags(curr_file.file);
	curr_file.total_time = tags->time;
	
	/* new title - curr_file.title - here */
	free(curr_file.title); /* free title. always. */
	if ( tags->title ) {
		curr_file.title = moc_build_title(tags);
	} else {
		curr_file.title = get_filename(curr_file.file);
	}
	
	/* parse polish characters in title */
	parse_unreadable_characters(curr_file.title);
	
	/* no need to store tags. i need only curr_file.title */
	tags_free(tags);
	return;
}

static void update_song_all_info() 
{
	/* song changed. Update song info */
	clock_stop(&timer_id);
	position = 0;
	
	free(curr_file.file);
	curr_file.file = get_curr_file();
	build_song_info(curr_file.file);
	curr_file.channels = get_channels () == 2 ? 2 : 1;
	curr_file.bitrate = get_bitrate();
	curr_file.rate = get_rate();
	curr_file.curr_time = get_curr_time ();
	
	timer_id = clock_start();
}

static void update_current_state ()  /* state of played file changed */
{
	/* it turns off timer if its not needed 
	 * and updates song informations */
	
	/* if previous state wasn't stop */
	if ( curr_file.state != STATE_STOP ) {
		/* then store current state in curr_file.state */
		/* if current state isn't stop */
		curr_file.state = get_state();
		update_song_all_info();
		if ( curr_file.state != STATE_STOP ) {
			char *next_song = get_curr_file();
			/* if current file is different from previous file */
			if ( strcmp( next_song, curr_file.file) != 0 ) {
				curr_file.file = next_song;
				update_song_all_info();
			}
		} else {
			/* current state is stop */
			/* no need to run clock during stop */
			position=0;
			clock_stop(&timer_id);
			file_info_reset(&curr_file);
		}
	} else {
		/* song started playing. Update whole song info */
		curr_file.state = get_state();
		update_song_all_info();
	}
}

static void server_event(int event, char *data) {
	if(debug) PDEBUGL(2, "EVENT from moc server: 0x%02x", event);
	switch (event) {
	case EV_BUSY:
		if(debug) logit ("The moc server is busy, another client is connected.");
		close(moc_sock);
		moc_sock = -2;
		view_in_this_loop=1;
		break;
	case EV_CTIME:
		curr_file.curr_time = get_curr_time ();
		view_in_this_loop=1;
		break;
	case EV_STATE:
		update_current_state();
		view_in_this_loop=1;
		break;
	case EV_EXIT:
		if(debug) logit ("Moc server exited.");
		close(moc_sock);
		moc_sock = -3;
		view_in_this_loop=1;
		break;
	case EV_BITRATE:
		curr_file.bitrate = get_bitrate();
		view_in_this_loop=1;
		break;
	case EV_RATE:
		if(debug) PDEBUGL(2, "updating rate");
		curr_file.rate = get_rate();
		view_in_this_loop=1;
		break;
	case EV_CHANNELS:
		curr_file.channels = get_channels() == 2 ? 2 : 1;
		view_in_this_loop=1;
		break;
	case EV_SRV_ERROR:
		if(debug) logit ("Moc server sended err message: %s.", get_err_mesg());
		close(moc_sock);
		moc_sock = -3;
		view_in_this_loop=1;
		break;
	case EV_OPTIONS:
		srv_options = get_server_options ();
		view_in_this_loop=1;
		break;
	case EV_TAGS:
		//build_song_info();
		update_curr_tags();
		// view_in_this_loop=1; // puff handled in update_curr_tags()	
		break;
	case EV_STATUS_MSG:
	case EV_SEND_PLIST:
	case EV_PLIST_ADD:
	case EV_PLIST_CLEAR:
	case EV_PLIST_DEL:
	case EV_PLIST_MOVE:
	case EV_MIXER_CHANGE:
	case EV_FILE_TAGS:
		break;
	default:
		break;
	}
	
	free(data);
}

static void dequeue_events () {/* Handle events from the queue. */
  struct event *e;
  while ((e = event_get_first(&events))) {
    server_event (e->type, e->data);
    event_pop (&events);
  }
}

static void get_and_handle_events () {
  int type;
  if(get_int_noblock(moc_sock, &type))
    server_event(type, handle_event_data(type));
  else /* error */
    moc_sock = -4;
}

static struct file_info init_current_file_state () {
	/* get all info about file */
	file_info_reset(&curr_file);
	curr_file.state = get_state();
	/* reboot clock, or stop it, if state==stop */
	clock_stop(&timer_id);
	if ( curr_file.state != STATE_STOP)  {
	
		curr_file.file = get_curr_file();
		build_song_info();
		curr_file.channels = get_channels () == 2 ? 2 : 1;
		curr_file.bitrate = get_bitrate();
		curr_file.rate = get_rate();
		curr_file.curr_time = get_curr_time ();
		
		timer_id = clock_start();
	}
	return(curr_file);
}

static void conn_moc_init() {
	int old_moc_sock = moc_sock;
	if((moc_sock = moc_server_connect()) < 0) {
		if(debug) PERR ("Can't connect to the moc server.");
		moc_sock = old_moc_sock;
	} else {
		if(debug) logit("Connected to moc server.\n");
	
		if (!ping_moc_server(moc_sock)) {
			if (debug) logit ("But can't ping moc server.");
			moc_sock = -4;
		} else {
			file_info_reset (&curr_file);
			event_queue_init (&events);
			init_current_file_state();
			srv_options = get_server_options ();
			send_int_to_srv (CMD_SEND_EVENTS);
			view_in_this_loop=1;
		}
	}
}

/************ end of moc server funtions. ********/

/****** lcd server functions *******/

static void handle_msglcd_handle_server() 
{
	int i;
	switch ( (i = msglcd_handle_server(lcd_sock)) ) {
	case -5:
	case -1:
		fatal("Strange error occured %d ", i);
	case -4:
		fatal("Server closed the connection. %d ", i);
	case -6:
		fatal("server sended error messege %d", i);
	case -2:
		fatal("Server closed the connection without sending exit mesg. %d ", i);
	case -3:
		fatal("my mesg has somth wrong %d ", i);
	}
}

void msglcd_send_cgram(char *pattern, unsigned int number, unsigned int screen) 
{
	char buff[] = { 
		[0] = 0x1b,
		[1] = '[',
		[2] = 'r',
		[3] = number + '0',
		[4] = screen,
		[5] = pattern[0],
		[6] = pattern[1], 
		[7] = pattern[2], 
		[8] = pattern[3], 
		[9] = pattern[4], 
		[10] = pattern[5], 
		[11] = pattern[6], 
		[12] = pattern[7] };
	if ( msglcd_send_to_lcd(lcd_sock, buff, 13) < 0 )  {
		fatal("msglcd_send_to_lcd");
	}
}

static void handle_msg_updat(const int sockfd, const char nr, const char *mesg, const int len) 
{
	int i;
	i = msglcd_updat(sockfd, nr, MSGLCD_PRIORITY, mesg, len);
	if ( i == 0 ) return;
	fatal("could not update mesg. msglcd_updat returned: %d", i);
}

static void move_print_pos(char *buff, int pos, const char *message) 
{
	const int len = strlen(message);
	const int screen_len = 40;
	if (strlen(message) <= screen_len) {
		const int offset = (( screen_len -len+1)/2);
		
		/* no need to run clock if message is longer then 40 chars */
		if ( timer_id )
			clock_stop(&timer_id);
		
		memset(buff, ' ', offset);
		mysnprintf(buff+offset, len, message);
		memset(buff + offset+len, ' ',  screen_len - (len+offset) );
		return;
	} else {
		int pos;
		int pre = 3, post = 3;
		int pre_time = 1, post_time = 2;
		int i;
		
		/* if timer down, start it */
		if (!timer_id) 
			timer_id = clock_start();
		
		
		if ( position-pre_time-pre-len+screen_len-post >= post_time ) /* reset position */
			position = 0;
		pos = position;
		
		
		if ( pos < pre_time ) {
			for(i = 0; i < pre; i++)
				buff[i] = ' ';
			mysnprintf(buff + pre, screen_len - pre, message);
		} else if ( (pos -= pre_time) < pre ) {
			pos = pre-pos;
			for(i=0; i < pos; i++)
				buff[i] = ' ';
			mysnprintf(buff + pos, screen_len + pos, message);
		} else if ( (pos -= pre) < len - screen_len ) {
			mysnprintf(buff, screen_len, message+pos);
		} else if ( (pos -= len - screen_len) < post ) {
			mysnprintf(buff, screen_len-pos, message + len - screen_len + pos);
			for(i=screen_len+pos; i < screen_len; i++)
				buff[i] = ' ';
		} else if ( (pos-=post) < post_time ) {
			mysnprintf(buff, screen_len-post, message + len - screen_len + post);
			for(i=screen_len-post; i < screen_len; i++)
				buff[i] = ' ';
		}
	}
}

/******end of lcd server functions ******/

static void moc_server_down() 
{
  /* moc_sock = -1 -> moc server is down */
  /* moc_sock = -2 -> moc server is busy */
  /* moc_sock = -3 -> moc_server sended EV_SRV_ERROR */
  /* moc_sock = -4 -> unspecifed error occured on moc server */
    switch (moc_sock) {
      case -1:
      case -4:
        handle_msg_updat(lcd_sock, 0, "         Nie ma muzyki.                             ", 40);
        handle_msg_updat(lcd_sock, 1, "                 Po prostu nie ma, nie pytaj.       ", 40);
        break;
      case -2:
        handle_msg_updat(lcd_sock, 0,  "Can't connect to moc server.                       ", 40);
        handle_msg_updat(lcd_sock, 1, "Moc server is busy. Try to lower number of clients. ", 40);
        break;
      case -3:
        handle_msg_updat(lcd_sock, 0, "Moc server sended err messege.                      ", 40);
        handle_msg_updat(lcd_sock, 1, "                                                    ", 40);
        break;
      case -5:
	handle_msg_updat(lcd_sock, 0, "Error in communication with moc server.             ", 40);
        handle_msg_updat(lcd_sock, 1, "                                                    ", 40);
      }
}

/****** parsing options functions ****/

static void parse_cmd(int argc, char *argv[]) 
{
	int c=0;
	extern char *optarg;
	extern int optind, opterr, optopt;
	opterr=0;
	while ( (c = getopt(argc, argv, "hdVi:")) != -1 ) {
		switch (c) {
		case 'h':
			printf("msgmoclcd: info about MusicOnConsole program\n"
				"sended to LCD geteway.\n"
				"\t-h\tdisplay this help.\n"
				"\t-d\tdebug mode on (do not fork into the background);\n"
				"\t-i<IP address>\t server ip address.\n");
			exit(0);
		case 'd':
			debug++;
			break;
		case 'V':
			printf("msgmocstat Version: %s \n", MSGMOCSTAT_VERSION);
			exit(0);
		case 'i':
			server_ip=inet_addr(optarg);
			break;
		}
	}
}

/***** end of parsing options funcitons ****/

static void view_on_lcd() 
{
	char buff[2][41] = { [0] [0 ... 40] = ' ' };
	mysnprintf(buff[1], 40, "[]00:00       [00:00]   KHz     Kbps    ");
	
	if (curr_file.state != STATE_STOP) {
		
		/* fill buff[0] with parsed current title */
		move_print_pos(buff[0], position, curr_file.title);
		
		/* file state play or pause */
		if (curr_file.state == STATE_PLAY)
			mysnprintf (buff[1], 2, "> ");
		else if (curr_file.state == STATE_PAUSE)
			mysnprintf(buff[1], 2, "||");
		
		/* current song time, left time, whole song time */
		if (curr_file.curr_time != -1) {
			char curr_time_str[6];
			sec_to_min (curr_time_str, curr_file.curr_time);
			mysnprintf(buff[1]+2, 5, "%5s", curr_time_str);
			if (curr_file.total_time != -1) {
				char full_time_str[6];
				char time_left_str[6];
				int left;
				left = curr_file.total_time - curr_file.curr_time;
				sec_to_min (time_left_str, left > 0 ? left : 0);
				sec_to_min (full_time_str, curr_file.total_time);
				mysnprintf(buff[1]+8, 12, "%5s [%5s", time_left_str, full_time_str);
			}
		} else {
			if (curr_file.total_time != -1) {
				char full_time_str[6];
				sec_to_min (full_time_str, curr_file.total_time);
				mysnprintf(buff[1]+8, 12, "??:?? [%5s", full_time_str);
			} else {
				mysnprintf(buff[1]+8, 12, "??:?? [??:??");
			}
		}
		
		
		/* fill the rest of informations */
		mysnprintf(buff[1]+22, 38, "%2dKHz %4dKbps %1s%1s%1s",
			curr_file.rate, curr_file.bitrate > 0 ? curr_file.bitrate : 0,
			srv_options.shuffle ? "s" : " ", srv_options.repeat ? "r" : " ", srv_options.autonext ? "n" : " ");
	}
	
	if(debug) {
		int i;
		for (i = 0; i < 40; ++i)
			printf("%c", buff[0][i]);
		printf("\n");
		for (i = 0; i < 40; ++i)
			printf("%c", buff[1][i]);
		printf("\n");
	}
	
	handle_msg_updat(lcd_sock, 0, buff[0], strlen(buff[0]));
	handle_msg_updat(lcd_sock, 1, buff[1], 40);
}

int daemonize(void) 
{
	int PID;
	PID = fork();
	switch ( PID ) {
	case 0:
		fclose(stderr);
		fclose(stdin);
		fclose(stdout);
		break;
	case -1:
		break;
	default:
		exit(0);
	}
	return(PID);
}


int main (int argc, char *argv[]) 
{
	parse_cmd(argc, argv);

	if (!server_ip) {
		fprintf(stderr, "Please give msgsrv ip address.\n");
		safe_exit();
	}
	if ((lcd_sock = msglcd_connect(server_ip)) < 0) {
		fprintf(stderr, "Can't connect to the lcd server.");
		safe_exit();
	}

	/* reserve two lines for us */
	handle_msg_updat(lcd_sock, 0, " ", 1);
        handle_msg_updat(lcd_sock, 1, " ", 1);

	logit("Connected with %s.\n", inet_ntoa(*(struct in_addr *)&server_ip));
	
	signal (SIGPIPE, SIG_IGN);
	signal (SIGQUIT, sig_hup);
	signal (SIGTERM, sig_hup);
	signal (SIGHUP, sig_hup);
	signal (SIGINT, sig_hup);
  
	clock_set_signal();
	clock_block_signal();
 
	if ( debug <= 0 )
		daemonize();

	conn_moc_init(moc_sock);

	for (;;) {
		while(moc_sock > 0) {
			fd_set fds;
			int ret = 0;
			struct timeval timeout = { 2, 0 };
			FD_ZERO (&fds);
			FD_SET (moc_sock, &fds);
			FD_SET (lcd_sock, &fds);
			/* dequeue events, if there where some */
			dequeue_events();
			/*if there is a reason to update lcd message, do it */
			if ( view_in_this_loop )
				view_on_lcd();
			view_in_this_loop=0;

			/* start receiving timer signal */
			if ( timer_id == 0 ) {
				/* no timer, we can check moc */
				ret = select ((moc_sock > lcd_sock ? moc_sock : lcd_sock) + 1, &fds, NULL, NULL, &timeout);
			} else {
				/* clock ticking */
				clock_unblock_signal();
				/* now I receive clock signal, if it were sended before clock_unblock_signal() */
				/* if i receive clock signal during select, then ret = -1 & errno = EINTR (see man select) */
				if(!clock_signal) 
					ret = select (moc_sock + 1, &fds, NULL, NULL, &timeout);
				/* stop receiving timer signal */
				/* i don't wanno handle it outside this place */
				clock_block_signal();
				if(clock_signal) {
					if(errno == EINTR) {
						/* actualy it was a succes, so */
						errno = 0;
						ret = 0;
					}
					if(debug) 
						PDEBUGL(2, "Clock signal received\n");
					clock_signal = 0;
					position++;
					view_in_this_loop=1;
				}
			}

			if (ret > 0) {
				if (FD_ISSET(moc_sock, &fds) ) {
					/* moc srv sended smth. */
					get_and_handle_events();
				}
				if (FD_ISSET(lcd_sock, &fds) ){
					/* lcd srv sended smth */
					handle_msglcd_handle_server();
				}
			} else if (ret == -1 && errno != EINTR) { /* this occurs least often ;) */
				if(errno == EBADF) { /* socket disconected from the other side */
					/* server have could been closed without sending me SRV_CLOSE */
					moc_sock = -4;
				} else {
					fatal ("Moc server select() failed: ");
				}
			}

		} /* while(moc_sock > 0) */

		/* moc_sock <= 0 */
		clock_stop(&timer_id);
		/* write smth on screen */
		moc_server_down();
		sleep(5);
		conn_moc_init();
	}
}


